#pragma once
//----------------------------------------------------------------------------------------------------------------
//			Filename:		RadiancePoint.h
//			  Author:		Adrian De Barro
//			    Date:		12/08/2015
//----------------------------------------------------------------------------------------------------------------
//			  Status: REFACTORING
//----------------------------------------------------------------------------------------------------------------
#include <string>
#include <math.h>

#include "..\..\CoreClasses\Point\Point3D\Point3D.h"
#include "..\..\CoreClasses\RGBObject\RGBColor.h"
#include "..\..\CoreClasses\Normal\Normal.h"
#include "..\..\CoreClasses\Ray\Ray.h"
#include "../../CoreClasses/UtilClasses/Common.h"
#include "../../CollisionDetails/ShadeRec.h"

enum DISTANCE_SURFACES
{
	HARMONIC_MEAN,
	MIN_RAY_LENGH
};

using std::string;

namespace Engine
{
	const float A_CONSTANT = 0.2;
	//----------------------------------------------------------------------------------------------------------------
	//			Class Name: RadiancePoint
	//		   Description: Indirect lighting light details for a single point.
	//----------------------------------------------------------------------------------------------------------------
	//			    Status: REFACTORING
	//----------------------------------------------------------------------------------------------------------------
	class RadiancePoint
	{
	public:
		Point3D m_position;
		Point3D hitPointNormal;
		float m_rayLength;
		RGBColor radiance;
		Ray m_collisionRay;
		string m_objectName;
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		RadiancePoint(void)
		{
			m_position = Point3D(0);
			hitPointNormal = Point3D();
			m_rayLength = 0;
			radiance = RGBColor(0);
			m_type = 0;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: Radiance Point position, Normal, Ray, Name
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		RadiancePoint(Point3D p_position, Point3D p_normal, Ray p_collsionRay = Ray(), string p_objectName = "")
		{
			m_position = p_position;
			hitPointNormal = p_normal;
			m_collisionRay = p_collsionRay;
			m_objectName = p_objectName;
			m_acquiredFrom = 0;
		}

		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CONSTRUCTOR
		//		Return Value: VOID
		//		  Parameters: Position, RayLength
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		RadiancePoint(Point3D p_position, float p_rayLength = INT_MAX)
		{
			m_position = p_position;
			m_rayLength = p_rayLength;
		}
		
		RadiancePoint(string p_input)
		{
			m_acquiredFrom = 1;
			FromString(p_input);
		}

		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: GetEpsillonHitPoint
		//		Return Value: Point3D
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		Point3D GetEpsillonHitPoint(void)
		{
			return m_position + Common::kEpsilon * hitPointNormal;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: GetWeighting
		//		Return Value: float
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		//Get Weigth : Calculate the weight of  the currnet Point with regards another point
		float GetWeighting(Point3D p_position, Point3D p_normal)
		{
			
			float length = (m_position - p_position).length();
			if (length < 0.1)
			{
				int test = 1 + 1;
			}

			float demA = (length/ m_rayLength);
		

			float demB = (sqrt(1 - DOT(hitPointNormal, p_normal)));
			float denominator =1 / max((demA + demB), 0.05f);
			return (denominator) - (1 / A_CONSTANT);
			//return denominator;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: ComputeRadiance
		//		Return Value: VOID
		//		  Parameters: vector<ShadRec>&
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		void ComputeRadiance(vector<ShadeRec>& radianceSamples)
		{
			for (int sampleIndex = 0; sampleIndex < radianceSamples.size(); sampleIndex++)
			{
				//RGBColor currentRadiance = radianceSamples[sampleIndex].
				radiance += radianceSamples[sampleIndex].myColor;
			}
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: ComputeRadianceNHarmonivValue
		//		Return Value: VOID
		//		  Parameters: vector<ShadeRec*>&, minRayLengths
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		void ComputeRadianceNHarmonivValue(vector<ShadeRec*>& p_ShadeRecList, float p_minRayLengths)
		{
			double invRaySummation = 0.0f;
			double minRayLength = INT_MAX;
			for (int sampleIndex = 0; sampleIndex < p_ShadeRecList.size(); sampleIndex++)
			{
				if (p_ShadeRecList[sampleIndex]->hitAnObject)
				{
					double rayVectorLength = (p_ShadeRecList[sampleIndex]->localHitPoint - m_position).length();

					minRayLength = min(rayVectorLength, minRayLength);
					radiance = radiance + p_ShadeRecList[sampleIndex]->myColor;
				}
			}
			if (minRayLength != 0)
				//m_rayLength = p_ShadeRecList.size() / invRaySummation;
				m_rayLength = max(minRayLength, p_minRayLengths);
			else
				m_rayLength = kEpsilon;
			//average
			if (radiance.r != 0 && radiance.b != 0 && radiance.g != 0)
				radiance = radiance / (int)p_ShadeRecList.size();

		}

		///-------------------------------------------------------------------------------------------------
		/// <summary>	Convert this object into a string representation. </summary>
		///
		/// <remarks>	Adrian, 17/09/2015. </remarks>
		///
		/// <returns>	A string that represents this object. </returns>
		///-------------------------------------------------------------------------------------------------

		string ToString() const
		{
			return radiance.ToString() + "^" + m_position.toString() + "^" + to_string(m_rayLength) + "^" + hitPointNormal.toString();
		}

		unsigned char* ToByteArray()
		{

		}

		void FromByteArray()
		{

		}


		void FromString(string p_stringInput)
		{
			string data[4];
			Common::Tokenizer::ParseString(p_stringInput, "^", data);

			m_acquiredFrom = 1;

			radiance.FromString(data[0]);
			m_position.FromString(data[1]);
			m_rayLength = stof(data[2]);
			hitPointNormal.FromString(data[3]);
		}

		void SerializePoint(unsigned char p_Array[], int p_startingPosition)
		{
			int incPos = p_startingPosition;

			radiance.SerializePoint(p_Array, incPos);
			incPos += 12;
			m_position.SerializePoint(p_Array, incPos);
			incPos += 12;
			ConvertionClass::ConvertFloat(m_rayLength, incPos, p_Array);
			incPos += 4;
			hitPointNormal.SerializePoint(p_Array, incPos);
		}

		void SerializePoint(vector<unsigned char>& p_Array)
		{
			radiance.SerializePoint(p_Array);
			m_position.SerializePoint(p_Array);
			ConvertionClass::ConvertFloat(m_rayLength, p_Array);
			hitPointNormal.SerializePoint(p_Array);
		}


		void FromSerialize(vector<unsigned char>& p_charArray)
		{
			radiance.FromSerialize(p_charArray);
			m_position.FromSerialize(p_charArray,12);
			ConvertionClass::ByteArrayToFloat(m_rayLength, p_charArray, 24, 4);
			hitPointNormal.FromSerialize(p_charArray, 28);

		}

		void FromSerialize(vector<unsigned char>& p_charArray, int p_startingPoint)
		{
			radiance.FromSerialize(p_charArray, p_startingPoint);
			m_position.FromSerialize(p_charArray, p_startingPoint+ 12);
			ConvertionClass::ByteArrayToFloat(m_rayLength, p_charArray, p_startingPoint+24, 4);
			hitPointNormal.FromSerialize(p_charArray, p_startingPoint+28);
		}

		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: CheckIfValidToPoint
		//		Return Value: bool
		//		  Parameters: RaidancePoint*
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		bool CheckIfValidToPoint(RadiancePoint* currentSample)
		{
			return false;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: GetHarmonicValue
		//		Return Value: VOID
		//		  Parameters: vector<ShadeRec>&
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		void GetHarmonicValue(vector<ShadeRec>& p_ShadeRecList)
		{
			double invRaySummation = 0.0f;

			for (int sampleIndex = 0; sampleIndex < p_ShadeRecList.size(); sampleIndex++)
			{
				if (p_ShadeRecList[sampleIndex].hitAnObject)
				{
					double rayVectorLength = (p_ShadeRecList[sampleIndex].hitPoint - m_position).length();

					invRaySummation += (1 / rayVectorLength);
				}
			}

			float rayLength = p_ShadeRecList.size() / invRaySummation;

			m_rayLength = rayLength;
		}
		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: operator
		//		Return Value: VOID
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		inline bool operator == (RadiancePoint* p_radiancePoint)
		{
			return m_position == p_radiancePoint->m_position;
		}
		inline bool operator == (Point3D p_position)
		{
			return m_position == p_position;
		}

		int GetType()
		{
			return m_type;
		}

		int GotFrom()
		{
			return m_acquiredFrom;
		}

		//----------------------------------------------------------------------------------------------------------------
		//		 Method Name: DESTRUCTOR
		//		Return Value: VOID
		//		  Parameters: VOID
		//			  Status: READY
		//----------------------------------------------------------------------------------------------------------------
		~RadiancePoint()
		{}

	private:
		int m_type = 0;
		int m_acquiredFrom = 0;

		/// <summary>	Size of the byte. </summary>
		int m_ByteSize = 55;
	};


	//----------------------------------------------------------------------------------------------------------------
}
//----------------------------------------------------------------------------------------------------------------

